/*
* Copyright 2012-2016 JetBrains s.r.o
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jetbrains.jetpad.projectional.domUtil;
import com.google.gwt.canvas.client.Canvas;
import com.google.gwt.canvas.dom.client.Context2d;
import com.google.gwt.canvas.dom.client.ImageData;
import com.google.gwt.user.client.Window;
import jetbrains.jetpad.geometry.DoubleVector;
import jetbrains.jetpad.values.Font;
import jetbrains.jetpad.values.FontFamily;
public class TextMetricsCalculator {
private static final String METRICS_TEST_STRING = "x";
public static String getFontName(FontFamily family) {
if (family == FontFamily.MONOSPACED) {
return "monospace";
} else if (family == FontFamily.SERIF) {
return "serif";
}
return family.toString();
}
public static TextMetrics calculateApprox(Font font) {
return calculateApprox(font, METRICS_TEST_STRING);
}
public static TextMetrics calculate(Font font) {
return calculate(font, METRICS_TEST_STRING);
}
public static TextMetrics calculateApprox(final Font font, String text) {
final DoubleVector dimension = calculateDimension(font, text);
return new TextMetrics() {
@Override
public DoubleVector dimension() {
return dimension;
}
@Override
public int baseLine() {
return (2 * font.getSize()) / 3 ;
}
};
}
public static TextMetrics calculate(Font font, String text) {
final DoubleVector dimension = calculateDimension(font, text);
final int baseLine = fontBaseLine(font);
return new TextMetrics() {
@Override
public DoubleVector dimension() {
return dimension;
}
@Override
public int baseLine() {
return baseLine;
}
};
}
private static DoubleVector calculateDimension(Font font, String text) {
return new DoubleVector(calculateWidth(font, text), adjustHeight(font.getSize()));
}
static double calculateWidth(Font font, String text) {
Canvas canvas = canvas();
Context2d ctx = canvas.getContext2d();
ctx.setFont(getFontString(font));
return ctx.measureText(normalize(text)).getWidth();
}
private static int adjustHeight(int height) {
String agent = Window.Navigator.getUserAgent().toLowerCase();
return agent.contains("firefox") ? height + 1 : height;
}
private static int fontBaseLine(Font font) {
int allCharsHeight = measureHeight(font, allCharsString());
int ascent = measureHeight(font, "A");
int lineSpace = font.getSize() - allCharsHeight;
return lineSpace / 2 + ascent;
}
static String normalize(String text) {
//replace space with
return text.replaceAll(" ", "\u00a0");
}
private static String allCharsString() {
StringBuilder allChars = new StringBuilder();
for (char c = 'a'; c <= 'z'; c++) {
allChars.append(c);
}
for (char c = 'A'; c <= 'Z'; c++) {
allChars.append(c);
}
return allChars.toString();
}
private static int measureHeight(Font font, String text) {
Canvas canvas = canvas();
Context2d ctx = canvas.getContext2d();
ctx.setFont(getFontString(font));
ctx.setFillStyle("rgb(255, 0, 0)");
int width = (int) ctx.measureText(text).getWidth();
int canvasHeight = font.getSize() * 2;
canvas.setHeight(canvasHeight + "px");
canvas.setHeight(font.getSize() * 2 + "px");
canvas.setWidth(width + "px");
ctx.fillText(text, 0, font.getSize());
ImageData data = ctx.getImageData(0, 0, width, canvasHeight);
int firstY = canvasHeight - 1;
int lastY = 0;
for (int x = 0; x < width; x++) {
for (int y = 0; y < canvasHeight; y++) {
int red = data.getRedAt(x, y);
if (red != 0) {
if (firstY > y) {
firstY = y;
}
if (lastY < y) {
lastY = y;
}
}
}
}
return lastY - firstY;
}
private static String getFontString(Font font) {
StringBuilder result = new StringBuilder();
if (font.isBold()) {
result.append(" bold");
}
if (font.isItalic()) {
result.append(" italic");
}
result.append(' ').append(font.getSize()).append("px ").append(getFontName(font.getFamily()));
return result.toString();
}
private static Canvas canvas() {
Canvas canvas = Canvas.createIfSupported();
if (canvas == null) {
throw new IllegalStateException();
}
return canvas;
}
}